home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / System 7.0 Samples / INIT - CDEV / CDEV.c next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  17.4 KB  |  531 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Macintosh Developer Technical Support
  4. #
  5. #    Sample Control Panel Device and INIT Combination
  6. #
  7. #    Program:    INIT - CDEV
  8. #    File:        CDEV.c    -    C Source
  9. #
  10. #    Copyright © 1990 Apple Computer, Inc.
  11. #    All rights reserved.
  12. #
  13. ------------------------------------------------------------------------------*/
  14.  
  15. #include <Common.h>
  16.  
  17. #include <Quickdraw.h>
  18. #include <Files.h>
  19. #include <Memory.h>
  20. #include <OSUtils.h>
  21. #include <TextEdit.h>
  22. #include <Devices.h>
  23. #include <Dialogs.h>
  24. #include <Packages.h>
  25. #include <TextUtils.h>
  26.  
  27. /*------------------------------------------------------------------------------
  28.     Constants
  29. ------------------------------------------------------------------------------*/
  30.  
  31. #define kNoINIT            -4048            /* Alert: INIT was not installed    */
  32. #define kCantWritePrefs    -4047            /* Alert: error writing preferences    */
  33.  
  34. #define kAboutText1        1
  35. #define kAboutText2        2
  36. #define kPrompt1        3
  37. #define kPrompt2        4
  38. #define kTimesToBeep    5                /* First editTExt item in cdev        */
  39. #define kBeepInterval    6
  40.  
  41.  
  42. /*------------------------------------------------------------------------------
  43.     Types
  44. ------------------------------------------------------------------------------*/
  45.  
  46. typedef struct CDEVRec {
  47.     CommonGlobalsPtr    commonGlobals;
  48. } CDEVRec, *CDEVPtr, **CDEVHnd;
  49.  
  50.  
  51. /*------------------------------------------------------------------------------
  52.     Procedure Prototypes and External Routines
  53. ------------------------------------------------------------------------------*/
  54.  
  55. /* These procedures are from the orginal EditTextCDEV sample. */
  56.  
  57. pascal    long    TextCDEV(short message, short item, short numItems,
  58.                         short CPanelID, EventRecord *theEvent,
  59.                         Handle cdevStorage, DialogPtr CPDialog);
  60.         void    DoEditCommand(short message, DialogPtr CPDialog);
  61.  
  62. /* Added for INIT - CDEV combo. */
  63.  
  64.         void    InitEditValues(CDEVHnd cdevStorage, short numItems,
  65.                                 DialogPtr CPDialog);
  66.         void    UpdateINIT(CDEVHnd cdevStorage, short numItems,
  67.                                 DialogPtr CPDialog);
  68.         void    UpdatePrefs(CDEVHnd cdevStorage);
  69.         long    GetItemValue(short itemNumber, DialogPtr CPDialog);
  70.         void    SetItemValue(short itemNumber, DialogPtr CPDialog, long value);
  71.         void    GetCommonStorage(CommonGlobalsPtr *commonGlobals);
  72.         OSErr    OpenAPort(short *refNum);
  73. pascal    void    StartCompProc(PPCParamBlockPtr ppb);
  74.  
  75.  
  76. /*------------------------------------------------------------------------------
  77.  
  78.     pascal long TextCDEV(short message, short item, short numItems,
  79.                         short CPanelID, EventRecord *theEvent,
  80.                         Handle cdevStorage, DialogPtr CPDialog);
  81.  
  82.     This is the main dispatcher. It must be the first code in the cdev.
  83.     EditCdev’s dispatcher responds only to the following messages from the
  84.     Control Panel:
  85.  
  86.     macDev        - To indicate what machines it is available on.
  87.     initDev        - To set up some temporary storage and get the caret started.
  88.     keyEvtDev    - To check for an edit command and do the appropriate action.
  89.     cutDev        - To cut the current selection.
  90.     copyDev        - To copy the current selection.
  91.     pasteDev    - To paste the contents of the clipboard.
  92.     clearDev    - To delete the current selection.
  93.     closeDev    - To dispose of any data allocated in initDev.
  94.  
  95.     The Dialog Manager’s services are used to handle entry of text, selection
  96.     of text, editing of text, and moving between the editText items via the
  97.     tab key. Since the Dialog Manager handles the selection of text, we do not
  98.     have to be concerned with hitDev messages for the editText items. The only
  99.     things we have to take care of are calling the Dialog Manager editing
  100.     routines in response to an edit command, and getting the caret to show up
  101.     at the beginning. In response to an edit command that was the result of a
  102.     command-key equivalent, we must also eliminate the event so that it does
  103.     not get processed as a keyDown by the Dialog Manager. Otherwise, an ‘x’
  104.     would show up in the editText item when the user did a command-x to cut
  105.     the text.
  106.  
  107. ------------------------------------------------------------------------------*/
  108.  
  109. pascal long TextCDEV(short message, short item, short numItems, short CPanelID,
  110.         EventRecord    *theEvent, Handle cdevStorage, DialogPtr CPDialog)
  111. {
  112. #pragma unused (item, CPanelID)            /* unused formal parameters */
  113.  
  114.     char                tempChar;
  115.     CommonGlobalsPtr    commonGlobals;
  116.     long                result;
  117.  
  118.     if (message == macDev) {
  119.         if (Gestalt(gestaltPPCToolboxAttr, &result))
  120.             return(0);                            /* No PPCToolbox; hide me */
  121.         else
  122.             return(1);                            /* Have PPCToolBox; show me */
  123.     }
  124.     else if (cdevStorage != nil) {
  125.         switch (message) {
  126.  
  127.             case initDev:
  128.                 GetCommonStorage(&commonGlobals);
  129.                 if (commonGlobals) {
  130.                     /* create private storage */
  131.                     cdevStorage = NewHandle(sizeof(CDEVRec));
  132.                     if (cdevStorage) {
  133.                         (**(CDEVHnd)cdevStorage).commonGlobals = commonGlobals;
  134.                         InitEditValues( (CDEVHnd) cdevStorage, numItems, CPDialog);
  135.                         /* make caret show up */
  136.                         SelectDialogItemText(CPDialog, numItems + kTimesToBeep, 0, 999);
  137.                     }
  138.                 } else {
  139.                     (void) StopAlert(kNoINIT, (ModalFilterProcPtr) nil);
  140.                     return(cdevGenErr);
  141.                 }
  142.                 break;
  143.  
  144.             case closeDev:                    /* clean up and dispose */
  145.                 UpdateINIT((CDEVHnd) cdevStorage, numItems, CPDialog);
  146.                 UpdatePrefs((CDEVHnd) cdevStorage);
  147.                 DisposeHandle(cdevStorage);
  148.                 break;
  149.  
  150.             case hitDev:                    /* handle hit on item */
  151.             case nulDev:
  152.             case updateDev:                    /* handle any update drawing */
  153.             case activDev:                    /* activate any needed items */
  154.             case deactivDev:                /* deactivate any needed items */
  155.                 break;
  156.  
  157.             case keyEvtDev:                    /* respond to keydown */
  158.                 tempChar = theEvent->message & charCodeMask;/* get the character, and check */
  159.                 if (theEvent->modifiers & cmdKey) {        /*  status of command key */
  160.                     message = nulDev;                    /* start with no message */
  161.                     theEvent->what = nullEvent;            /* and empty event type */
  162.  
  163.                     switch (tempChar) {                    /* set appropriate message */
  164.  
  165.                         case 'X':
  166.                         case 'x':
  167.                             message = cutDev;
  168.                             break;
  169.                         case 'C':
  170.                         case 'c':
  171.                             message = copyDev;
  172.                             break;
  173.                         case 'V':
  174.                         case 'v':
  175.                             message = pasteDev;
  176.                             break;
  177.                     }
  178.                     DoEditCommand(message, CPDialog);    /* Let edit command handler take it */
  179.                 }
  180.                 break;
  181.  
  182.             case macDev:
  183.             case undoDev:
  184.                 break;
  185.  
  186.             case cutDev:
  187.             case copyDev:
  188.             case pasteDev:
  189.             case clearDev:
  190.                 DoEditCommand(message, CPDialog);        /* respond to edit command */
  191.                 break;
  192.         }
  193.  
  194.         return ((long) cdevStorage);
  195.     }  /* cdevStorage != nil */
  196.  
  197.     /*
  198.     **    We get here iff cdevStorage = NIL. Return 0 so that Control Panel
  199.     **  will put up "out of memory" error
  200.     */
  201.     return (0);
  202. }
  203.  
  204.  
  205. /*------------------------------------------------------------------------------
  206.  
  207.     void DoEditCommand (short message, DialogPtr CPDialog)
  208.  
  209.     Call the appropriate Dialog Manager routine to handle an edit command for
  210.     an editText item. It will do all the work regarding the TEScrap.
  211.  
  212. ------------------------------------------------------------------------------*/
  213.  
  214. void DoEditCommand (short message, DialogPtr CPDialog)
  215. {
  216.     switch (message) {
  217.         case cutDev:
  218.             DialogCut(CPDialog);
  219.             break;
  220.         case copyDev:
  221.             DialogCopy(CPDialog);
  222.             break;
  223.         case pasteDev:
  224.             DialogPaste(CPDialog);
  225.             break;
  226.         case clearDev:
  227.             DialogDelete(CPDialog);
  228.             break;
  229.     }
  230. }
  231.  
  232.  
  233. /*------------------------------------------------------------------------------
  234.  
  235.     void InitEditValues(CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog)
  236.  
  237.     Called when the CDEV is opened. It uses the pointer to the public globals
  238.     that we got from the INIT to get the values to stuff into the EditText
  239.     items.
  240.  
  241. ------------------------------------------------------------------------------*/
  242.  
  243. void    InitEditValues (CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog)
  244. {
  245.     CommonGlobalsPtr    commonGlobals;
  246.  
  247.     commonGlobals = (**cdevStorage).commonGlobals;
  248.     SetItemValue(numItems + kTimesToBeep,            /* item to set */
  249.                 CPDialog,                            /* of this dialog */
  250.                 commonGlobals->timesToBeep);        /* set to this value */
  251.     SetItemValue(numItems + kBeepInterval,            /* item to set */
  252.                 CPDialog,                            /* of this dialog */
  253.                 commonGlobals->beepInterval / 60);    /* set to this value */
  254. }
  255.  
  256.  
  257. /*------------------------------------------------------------------------------
  258.  
  259.     void UpdateINIT(CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog)
  260.  
  261.     Called when the CDEV is about to close. It gets the values from the
  262.     EditText items and stuffs them back into the INIT's public globals.
  263.  
  264. ------------------------------------------------------------------------------*/
  265.  
  266. void     UpdateINIT (CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog)
  267. {
  268.     CommonGlobalsPtr    commonGlobals;
  269.  
  270.     commonGlobals = (**cdevStorage).commonGlobals;
  271.     commonGlobals->timesToBeep = GetItemValue(numItems + kTimesToBeep, CPDialog);
  272.     commonGlobals->beepInterval = GetItemValue(numItems + kBeepInterval, CPDialog) * 60;
  273. }
  274.  
  275.  
  276. /*------------------------------------------------------------------------------
  277.  
  278.     void UpdatePrefs(CDEVHnd cdevStorage)
  279.  
  280.     Called when the CDEV is being closed. This routine writes to a preferences
  281.     file, using the INIT's public globals as the output buffer. The data is
  282.     written to the data fork; no resources are used at all. If the preferences
  283.     file doesn't exist, it is created. Any errors are reported through an
  284.     Alert saying that error number such-and-such occured.
  285.  
  286. ------------------------------------------------------------------------------*/
  287.  
  288. void    UpdatePrefs(CDEVHnd cdevStorage)
  289. {
  290.     OSErr        err;
  291.     FSSpec        spec;
  292.     short        refNum;
  293.     long        amountToWrite;
  294.     Str255        errString;
  295.  
  296.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
  297.                     &spec.vRefNum, &spec.parID);
  298.     if (err == noErr) {
  299.         (void) PLstrcpy(spec.name, kPrefsFileName);
  300.         err = FSpCreate(&spec, kCreator, kDocKind, smSystemScript);
  301.         if ((err == noErr) || (err == dupFNErr)) {
  302.             err = FSpOpenDF(&spec, fsRdWrPerm, &refNum);
  303.             if (err == noErr) {
  304.                 amountToWrite = sizeof(CommonGlobalsRec);
  305.                 err = FSWrite(refNum, &amountToWrite,
  306.                                 (Ptr) (**cdevStorage).commonGlobals);
  307.                 (void) FSClose(refNum);
  308.             }
  309.         }
  310.     }
  311.  
  312.     if (err) {
  313.         NumToString(err, errString);
  314.         ParamText(errString, nil, nil, nil);
  315.         (void) StopAlert(kCantWritePrefs, (ModalFilterProcPtr) nil);
  316.     }
  317. }
  318.  
  319.  
  320. /*------------------------------------------------------------------------------
  321.  
  322.     long GetItemValue(short itemNumber, DialogPtr CPDialog)
  323.  
  324.     Utility routine used to turn the text in an EditText item into a long,
  325.     and return it.
  326.  
  327. ------------------------------------------------------------------------------*/
  328.  
  329. long    GetItemValue(short itemNumber, DialogPtr CPDialog)
  330. {
  331.     short        itemType;
  332.     Handle        itemHandle;
  333.     Rect        itemRect;
  334.     Str255        text;
  335.     long        theNum;
  336.  
  337.     GetDialogItem(CPDialog, itemNumber, &itemType, &itemHandle, &itemRect);
  338.     GetDialogItemText(itemHandle, text);
  339.     StringToNum(text, &theNum);
  340.     return(theNum);
  341. }
  342.  
  343.  
  344. /*------------------------------------------------------------------------------
  345.  
  346.     void SetItemValue(short itemNumber, DialogPtr CPDialog, long value)
  347.  
  348.     Utility routine used to turn a short into text, and stuff it into
  349.     an EditText item
  350.  
  351. ------------------------------------------------------------------------------*/
  352.  
  353. void    SetItemValue(short itemNumber, DialogPtr CPDialog, long value)
  354. {
  355.     short        itemType;
  356.     Handle        itemHandle;
  357.     Rect        itemRect;
  358.     Str255        text;
  359.  
  360.     NumToString(value, text);
  361.     GetDialogItem(CPDialog, itemNumber, &itemType, &itemHandle, &itemRect);
  362.     SetDialogItemText(itemHandle, text);
  363. }
  364.  
  365.  
  366. /*------------------------------------------------------------------------------
  367.  
  368.     void GetCommonStorage(CommonGlobalsPtr *commonGlobals)
  369.  
  370.     This routine is called when the CDEV is opend to get a pointer to the
  371.     INIT's public globals. During the life of the INIT, this pointer is  saved
  372.     in a block referenced by a handle. This handle is saved for us by the
  373.     Control Panel.
  374.  
  375.     We get the pointer to the INIT's public globals by using the PPCToolbox.
  376.     Because we wrote the INIT, we know that it is identifying itself to us by
  377.     creator/type. So we fill out a portName with that information, and make an
  378.     asynchronous PPCStart call. We make the call asynchronously so that the
  379.     PPCToolbox can go an process all of the subsequent Read/Write/
  380.     End/Inform/etc. calls required in a PPC transaction. However, we have to
  381.     hang around for the PPCStart call to finish, so we poll ioResult until it
  382.     no longer equal 1 (ioResult = 1 is the international symbol for "I'm
  383.     working on a task right now, come back later when I'm done.").
  384.  
  385.     When the PPCStart call completes, this means that the completion routine
  386.     we installed, StartCompProc, has been called by the PPCToolbox. It is
  387.     StartCompProc that was responsible for reading the data being sent to us
  388.     by the INIT. Therefore, when PPCStart completes, our data buffer should
  389.     have the location of the INIT's public globals.
  390.  
  391. ------------------------------------------------------------------------------*/
  392.  
  393. void    GetCommonStorage(CommonGlobalsPtr *commonGlobals)
  394. {
  395.     OSErr                err;
  396.     SessionRecord        sessionRecord;
  397.     short                refNum;
  398.  
  399.     *commonGlobals = nil;
  400.  
  401.     err = OpenAPort(&refNum);
  402.     if (err) return;
  403.  
  404.     sessionRecord.portName.nameScript = GetScriptManagerVariable(smSysScript);
  405.     (void) PLstrcpy(sessionRecord.portName.name, "\pBG Beeper");
  406.     sessionRecord.portName.portKindSelector = ppcByCreatorAndType;
  407. #if GENERATING68K
  408.     // Universal Interfaces 2.0
  409.     sessionRecord.portName.u.port.portCreator = kCreator;
  410.     sessionRecord.portName.u.port.portType = 'INIT';
  411. #else
  412.     sessionRecord.portName.u.port.creator = kCreator;
  413.     sessionRecord.portName.u.port.type = 'INIT';
  414. #endif
  415.     sessionRecord.pb.startParam.ioCompletion    = (PPCCompProcPtr) StartCompProc;
  416.     sessionRecord.pb.startParam.portRefNum        = refNum;
  417.     sessionRecord.pb.startParam.serviceType        = ppcServiceRealTime;
  418.     sessionRecord.pb.startParam.resFlag            = 0;
  419.     sessionRecord.pb.startParam.portName        = &sessionRecord.portName;
  420.     sessionRecord.pb.startParam.locationName    = nil;
  421.     sessionRecord.pb.startParam.userData        = kGetCommonGlobalsPtr;
  422.     sessionRecord.pb.startParam.userRefNum        = 0;    /* guest access? */
  423.  
  424.     err = PPCStartAsync(&sessionRecord.pb.startParam);
  425.  
  426.     /*    Even though we make an asynchronous call, we have to wait
  427.         around for the call to complete. One reason for this is because
  428.         we can't really do anything until we get the information we are
  429.         asking for from the INIT. Another good reason is because our
  430.         parameter block is allocated on the stack. If we were to leave
  431.         now, our parameter block would vanish in a puff of orange smoke,
  432.         along with the rest of the Operating System... */
  433.  
  434.     while (sessionRecord.pb.startParam.ioResult == 1) ;        /* wait for the reply */
  435.  
  436.     if (sessionRecord.pb.startParam.ioResult == noErr)
  437.         *commonGlobals =
  438.             ((GetCommonGlobalsPtr) &sessionRecord.buffer)->commonGlobalsAddress;
  439.  
  440.     err = PPCEnd(&sessionRecord.pb.endParam, false);
  441.     err = PPCClose(&sessionRecord.pb.closeParam, false);
  442. }
  443.  
  444.  
  445. /*------------------------------------------------------------------------------
  446.  
  447.     OSErr OpenAPort(short *refNum)
  448.  
  449.     Used to open a PPC port. We identify ourselves using the portName record.
  450.     First, we give ourselves a name that will show up in the PPCBrowser
  451.     (portName.name). We then give ourselves an identify to other processes. We
  452.     can do this either by name or by creator/type signatures. In our case, we
  453.     choose creator/type ('INCD'/'CDEV').
  454.  
  455.     We then set up for a PPCOpen call. We are making a synchronous call, so we
  456.     don't need a completion routine. ServiceType and resFlag are set to
  457.     required values (per Inside Mac). We make the CDEV not visible over the
  458.     network. Next, we point to the name record we want to use for our port,
  459.     and use the default location name (which identifies our computer to other
  460.     computers on the network).
  461.  
  462.     Finally, we make the PPCOpen call synchronously, and return any errors.
  463.  
  464. ------------------------------------------------------------------------------*/
  465.  
  466. OSErr OpenAPort(short *refNum)
  467. {
  468.     PPCPortRec        thePort;
  469.     PPCOpenPBRec    pb;
  470.     OSErr            err;
  471.  
  472.     thePort.nameScript = GetScriptManagerVariable(smSysScript);
  473.     (void) PLstrcpy(thePort.name, "\pBG Beeper");
  474.     thePort.portKindSelector = ppcByCreatorAndType;
  475. #if GENERATING68K
  476.     // Universal Interfaces 2.0
  477.     thePort.u.port.portCreator = kCreator;
  478.     thePort.u.port.portType = 'CDEV';
  479. #else
  480.     thePort.u.port.creator = kCreator;
  481.     thePort.u.port.type = 'CDEV';
  482. #endif
  483.  
  484.     pb.ioCompletion = nil;
  485.     pb.serviceType = ppcServiceRealTime;
  486.     pb.resFlag = 0;
  487.     pb.networkVisible = true;
  488.     pb.portName = &thePort;
  489.     pb.locationName = nil;            // use the default location
  490.  
  491.     err = PPCOpen(&pb, false);
  492.     if (err) return(err);
  493.  
  494.     *refNum = pb.portRefNum;
  495.     return(noErr);
  496. }
  497.  
  498.  
  499. /*------------------------------------------------------------------------------
  500.  
  501.     pascal void StartCompProc(PPCParamBlockPtr ppb)
  502.  
  503.     When the PPCStart call we made in GetCommonStorage completes, the
  504.     PPCToolbox calls the completion routine we specified (this routine). At
  505.     this point, our INIT has been contacted, because it had an outstanding
  506.     PPCInform call. The INIT's PPCInform completion routine looked at what we
  507.     stuffed in the userData field when we made the PPCStart call, and wrote
  508.     back to us the location of its public globals with PPCWrite. Therefore, we
  509.     have to set ourselves up to read that data with PPCRead. That's what we do
  510.     here.
  511.  
  512.     Because this is a completion routine, it is declared as using the Pascal
  513.     calling convention.
  514.  
  515. ------------------------------------------------------------------------------*/
  516.  
  517. pascal void StartCompProc(PPCParamBlockPtr ppb)
  518. {
  519.     OSErr                err;
  520.     GetCommonGlobalsPtr    myBuffer;
  521.  
  522.     myBuffer = (GetCommonGlobalsPtr) ((SessionPtr)ppb)->buffer;
  523.  
  524.     ppb->readParam.ioCompletion    = nil;
  525.     ppb->readParam.bufferLength    = sizeof(GetCommonGlobalsRecord);
  526.     ppb->readParam.bufferPtr    = (Ptr) myBuffer;
  527.     ppb->readParam.more            = false;
  528.  
  529.     err = PPCReadAsync(&ppb->readParam);
  530. }
  531.